﻿#region Namespace References

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml;
using DynConDevCorp.OpenArch.SmartData.Core;
using DynConDevCorp.OpenArch.SmartData.Interfaces;

#endregion

namespace DynConDevCorp.OpenArch.SmartData.Persistance
{
    /// <summary>
    /// 
    /// </summary>
    public class SmartGraphReaderXml : StructuredXmlReader, ISmartDispatcher
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SmartGraphReaderXml"/> class.
        /// </summary>
        /// <param name="xmlReader">The XML reader.</param>
        public SmartGraphReaderXml(XmlReader xmlReader) : base(xmlReader)
        {
            m_ObjectGraph = new SmartEntityInstanceGraph();
        }

        #endregion

        #region Public Instance Methods

        /// <summary>
        /// Reads all.
        /// </summary>
        /// <returns></returns>
        public SmartEntityInstanceGraph ReadAll()
        {
            ReadNode(); //Prime Pump...
            ReadNode(); // Skip XmlDeclaration
            ElementDispatch dispatch = new ElementDispatch
                                           {
                                               {"SmartObjectGraph", ProcessSmartObjectGraph}
                                           };
            ProcessSegment(String.Empty, dispatch);
            return m_ObjectGraph;
        }

        #endregion

        #region Private Instance Methods

        private void ProcessCollection(string parentElementName)
        {
            ElementDispatch dispatch = new ElementDispatch
                                           {
                                               {"Name", ProcessCollectionName},
                                               {"Contents", ProcessContents},
                                           };
            ProcessSegment(parentElementName, dispatch);
        }

        private void ProcessCollectionContents(string parentElementName)
        {
            ElementDispatch dispatch = new ElementDispatch
                                           {
                                               {"Collection", ProcessCollection},
                                           };
            ProcessSegment(parentElementName, dispatch);
        }

        private void ProcessCollectionEntity(string parentElementName)
        {
            Guid EntityID = new Guid((string) ReadNodeValue());
            m_ObjectGraph.AddEntityToCollection(m_ActiveCollectionName, EntityID);
        }

        class FieldValueContext
        {
            public object FieldVaLue;
        }

        private void ProcessCollectionName(string parentElementName)
        {
             m_ActiveCollectionName = (string)ReadNodeValue();
        }

        private string m_ActiveCollectionName;
        private void ProcessCollectionNameDeclaration(string parentElementName)
        {
            string collectionName = (string)ReadNodeValue();
            ISmartEntityCollection<SmartDynamicEntity> collection = new SmartEntityCollection<SmartDynamicEntity>();
            m_ObjectGraph.AddCollection(collectionName, collection);
            ReadEndNode(parentElementName);
        }

        private void ProcessCollectionNames(string parentElementName)
        {
            ElementDispatch dispatch = new ElementDispatch
                                           {
                                               {"Name", ProcessCollectionNameDeclaration}
                                           };
            ProcessSegment(parentElementName, dispatch);
        }

        private void ProcessContents(string parentElementName)
        {
            ElementDispatch dispatch = new ElementDispatch
                                           {
                                               {"EntityID", ProcessCollectionEntity},
                                           };
            ProcessSegment(parentElementName, dispatch);
        }

        private void ProcessEntities(string parentElementName)
        {
            ElementDispatch dispatch = new ElementDispatch
                                           {
                                               {"Entity", ProcessEntity}
                                           };
            ProcessSegment(parentElementName, dispatch);
        }

        private void ProcessEntity(string parentElementName)
        {
            ElementDispatch dispatch = new ElementDispatch
                                           {
                                               {"EntityID", ProcessEntityID},
                                               {"MetaEntityTypeName", ProcessMetaEntityTypeName},
                                               {"FieldValues", ProcessFieldValues}
                                           };
            ProcessSegment(parentElementName, dispatch);
            m_ObjectGraph.AddEntity(m_CurrentEntity);
            m_CurrentEntity = null;
        }

        

        private void ProcessEntityID(string parentElementName)
        {
            Guid EntityID = new Guid((string)ReadNodeValue());
            m_CurrentEntity = (ISmartEntity) m_CurrentEntityConstructor.Invoke(new object[] {EntityID});
        }

        private string m_CurrentMetaEntityTypeName;
        private Type m_CurrentMetaEntityType;
        private ConstructorInfo m_CurrentEntityConstructor;

        private void ProcessMetaEntityTypeName(string parentElementName)
        {
            string currentTypeName = (string) ReadNodeValue();
            if (currentTypeName != m_CurrentMetaEntityTypeName)
            {
                m_CurrentMetaEntityTypeName = currentTypeName;
                foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                {
                    Type type = assembly.GetType(m_CurrentMetaEntityTypeName);
                    if (type != null)
                    {
                        ConstructorInfo constructorInfo = type.GetConstructor(new[] { typeof(Guid) });
                        if (constructorInfo != null)
                        {
                            m_CurrentMetaEntityType = type;

                            m_CurrentEntityConstructor = constructorInfo;
                        }
                    }
                }
            }
        }

        private void ProcessFieldValues(string parentElementName)
        {
            bool done = false;
            string currentFieldName = null;
            ISmartField currentField = null;
            while (!done)
            {
                ReadNode();
                switch (m_CurrentNodeType)
                {
                    case XmlNodeType.Element:
                        currentFieldName = m_CurrentNodeName;
                        // Fields should all be created from MetaData...
                        //currentField = new SmartField<String>();
                        //m_CurrentEntity.RegisterField(currentFieldName, currentField);
                        currentField = m_CurrentEntity[currentFieldName];
                        break;
                    case XmlNodeType.Text:
                        FieldValueContext fieldValueContext = new FieldValueContext
                                                                  {
                                                                      FieldVaLue = m_CurrentNodeValue
                                                                  };
                        currentField.Dispatch(this, fieldValueContext);
                        break;
                    case XmlNodeType.EndElement:
                        if (m_CurrentNodeName == parentElementName)
                        {
                            done = true;
                        }
                        else if (currentFieldName != null)
                        {
                            if (m_CurrentNodeName == currentFieldName)
                                currentFieldName = null;
                            else
                                throw new Exception();
                        }
                        else
                        {
                            throw new Exception();
                        }
                        break;
                    default:
                        throw new Exception();
                }
            }
        }


        ////private void ProcessSegment(string parentElementName, ElementDispatch dispatch)
        ////{
        ////    bool done = false;
        ////    string currentElementName = null;
        ////    while (!done)
        ////    {
        ////        ReadNode();
        ////        switch (m_CurrentNodeType)
        ////        {
        ////            case XmlNodeType.Element:
        ////                {
        ////                    currentElementName = m_CurrentNodeName;
        ////                    ElementHandler handler;
        ////                    if (dispatch.TryGetValue(currentElementName, out handler))
        ////                        handler(currentElementName);
        ////                    else
        ////                        throw new Exception();
        ////                }
        ////                break;
        ////            case XmlNodeType.EndElement:
        ////                if (m_CurrentNodeName == parentElementName)
        ////                {
        ////                    if ((m_PeekNodeType == XmlNodeType.Element) && dispatch.ContainsKey(m_PeekNodeName))
        ////                    {
        ////                    }
        ////                    else
        ////                    {
        ////                        done = true;
        ////                    }
        ////                }
        ////                else if (m_CurrentNodeName != currentElementName)
        ////                {
        ////                    throw new Exception();
        ////                }
        ////                break;
        ////            case XmlNodeType.None:
        ////                done = true;
        ////                break;
        ////            default:
        ////                throw new Exception();
        ////        }
        ////    }
        ////}

        private void ProcessSmartObjectGraph(string parentElementName)
        {
            ElementDispatch dispatch = new ElementDispatch
                                           {
                                               {"Entities", ProcessEntities},
                                               {"CollectionNames", ProcessCollectionNames},
                                               {"CollectionContents", ProcessCollectionContents}
                                           };
            ProcessSegment(parentElementName, dispatch);
        }

        #endregion

        #region Fields and Constants

        private readonly SmartEntityInstanceGraph m_ObjectGraph;
        private ISmartEntity m_CurrentEntity;

        #endregion

        #region ISmartDispatcher Members

        void ISmartDispatcher.ProcessDispatch(ISmartDisplayFormatter target, object context)
        {
            throw new NotImplementedException();
        }

        void ISmartDispatcher.ProcessDispatch(ISmartNullableField target, object context)
        {
            throw new NotImplementedException();
        }

        void ISmartDispatcher.ProcessDispatch(ISmartField target, object context)
        {
            FieldValueContext fvc = (FieldValueContext)context;
            target.RawValue = fvc.FieldVaLue;
        }

        void ISmartDispatcher.ProcessDispatch(ISmartEntityLink target, object context)
        {
            FieldValueContext fvc = (FieldValueContext)context;
            Guid linkedEntityID = new Guid((String)fvc.FieldVaLue);
            ISmartEntity linkedEntity = GetEntity(linkedEntityID);
            target.SetRawLinkedEntity(linkedEntity);
        }

        void ISmartDispatcher.ProcessDispatch(ISmartEntityCollection target, object context)
        {
            throw new NotImplementedException();
        }

        void ISmartDispatcher.ProcessDispatch(ISmartEntity target, object context)
        {
            throw new NotImplementedException();
        }


        private ISmartEntity GetEntity(Guid entityID)
        {
            ISmartEntity retVal = m_ObjectGraph.GetEntity(entityID);
            if (retVal == null)
                throw new Exception();
            return retVal;
        }
        #endregion
    }
}